//
//  vdKernel.h
//  Void Dead
//
//  Created by Sidney Just on 16.11.09.
//
//  Copyright © 2009 by Sidney Just
//  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 
//  documentation files (the "Software"), to deal in the Software without restriction, including without limitation 
//  the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, 
//  and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
//  The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 
//  INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 
//  PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 
//  FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 
//  ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//


#import <Foundation/Foundation.h>

#import "vdHeader.h"

#import "vdES1.h"
#if defined(USE_OS_30) || defined(USE_OS_32) || defined(USE_OS_40) || USE_OS_32 || USE_OS_40
#import "vdES2.h"
#endif

#if defined(USE_OS_32) || defined(USE_OS_40)
typedef void (^vd_block)(void);

typedef struct {
	vd_block	block;
	int			timeStep;
} VDQEUESTRUCT;

typedef struct {
	VDQEUESTRUCT	*queue;
	int				timeLeft;
	BOOL			autoDelete;
} VDDISPATCHSTRUCT;


typedef VDQEUESTRUCT			vd_queue;
typedef VDDISPATCHSTRUCT	vd_dispatch;

#endif

#import "vdShader.h"

typedef enum {
	vdCapsShader = 1,
	vdCapsTexture1024 = 2,
	vdCapsTexture2048 = 4,
	vdCapsDisplayLink = 8
} vdEngineCaps;

/**
 * A render target is a framebuffer which renders into a texture (rtt)
 **/
typedef struct {
	/**
	 * Handle to the framebuffer
	 **/
	GLuint		Framebuffer;
	/**
	 * Handle to the texture where the framebuffer renders into
	 **/
	GLuint		Texture;
	/**
	 * Array of post processing shader
	 **/
	vdShader	**postShader;
	/**
	 * The amount of post processing shader
	 **/
	int			postShaders;
	/**
	 * If this is set to YES (default when creating a render target via the vdKernel), the framebuffer will be cleared before anything is drawn into.
	 **/
	BOOL		clearBuffer;
} vdRenderTarget;

void setAtctiveRenderTarget(vdRenderTarget *target);

@class vdNode;

#import "vdRenderer.h"
#import "vdEventHandler.h"
#import "vdTexManager.h"
#import "vdShaderLib.h"
#import "vdMacros.h"

/**
 * Main Class thats create and handles the render view. It's also starts and handles the rendering process.
 * <br>There can only be one vdKernel object at a time! This object will be always the sharedKernel and can be accessed via [vdRenderer sharedKernel]. 
 **/
@interface vdKernel : NSObject {
	//\cond
	NSTimeInterval deltaTime;
	int fps;
	
	UIInterfaceOrientation orientation;
	
	vdRenderingAPI rAPI;
	vdView *renderView;
	
	unsigned int engineCaps;
	NSString *version;
	unsigned long tick;
	
	BOOL useDisplayLink;
	
	//\endcond
@private
	vdNode *renderNode;
	
	id displayLink;
	NSTimer *renderTimer;
	NSTimeInterval *userDelta;
	
	NSString *oglVersion;
	
	float maxFPS;
	
	vdEventHandler *handler;
	
	// Post Processing
	vdRenderTarget **renderTarget;
	int renderTargets;
	
#if defined(USE_OS_32) || defined(USE_OS_40)
	vd_dispatch *dispatchList;
	int listLength;
#endif
}

/**
 * The used rendering API. This can be: <br>
 * OpenGLESAPI1 = OpenGL ES 1.1 <br>
 * OpenGLESAPI2 = OpenGL ES 2.0
 **/
@property (readonly) vdRenderingAPI rAPI;

/**
 * If this is set to YES (default) and the CADisplayLink class exist (since iPhone OS 3.1), the engine will use a CADisplayLink to fire the drawScene function.
 * Otherwise it will use a NSTimer. If you change this variable at runtime, the render loop will restart.
 **/
@property BOOL useDisplayLink;

/**
 * The capabilities of the hardware/engine as logical OR bit field 
 * <br>vdCapsShader = The engine can use shaders
 * <br>vdCapsTexture1024 = The maximum texture size is 1024
 * <br>vdCapsTexture2048 = The maximum texture size is 2048 (when you compile for iPhone OS 2.x this flag isn't set, even if the hardware is capable to load 2048 textures)
 * <br>vdCapsDisplayLink = The engine is capable to the new CADisplayLink class (since iPhone OS 3.1).
 **/
@property (readonly) unsigned int engineCaps;

/**
 * The current frame since the last startRendering call.
 * <br>(If you think this var will overflow after a few hours, the var will overflow after (((2^64)-1)/60)/60 hours (if you have a constant FPS of 60) This is _MUCH_!)
 **/
@property (readonly) unsigned long tick;

/**
 * The view in which the engine renders into
 **/
@property (readonly, nonatomic) vdView *renderView;

/**
 * The version of the engine as NSString
 **/
@property (readonly, nonatomic) NSString *version;

/**
 * The current fps.
 **/
@property (readonly) int fps;

/**
 * The orientation of the engine as UIInterfaceOrientation value
 **/
@property (readonly) UIInterfaceOrientation orientation;

/**
 * The seconds which has been needed to draw the last frame
 **/
@property NSTimeInterval deltaTime;

/**
 * Returns the default vdKernel object.
 @return Pointer to the default/shared vdKernel object
 @remark If no kernel exist, this function will create one with OpenGL ES 2.0 as API
 **/
+ (vdKernel *)sharedKernel;

/**
 * Inits the vdKernel with the given OpenGL ES API.
 @param kApi Possible values are OpenGLESAPI1 or OpenGLESAPI2.
 @remark When the API isn't supported (either because the hardware isn't cappable or USE_OS_30 or USE_OS_40 isn't defined) the vdKernel tries to fallback to a lower version.
 @return Pointer to the new vdKernel (which will become the sharedKernel) or NULL if there is already a vdKernel.
 **/
- (id)initWithAPI:(vdRenderingAPI)kApi;

/**
 * @deprecated Replaced by [vdKernel attachToWindow:]
 **/
- (void)attachViewToWindow:(UIWindow *)aWindow __attribute__((deprecated));

/**
 * @deprecated Replaced by [vdKernel attachToView:]
 **/
- (void)attachViewToView:(UIView *)aView __attribute__((deprecated));

/**
 * Adds the rendering view into the given window
 @param aWindow The window where the render view will added into
 **/
- (void)attachToWindow:(UIWindow *)aWindow;

/**
 * Adds the rendering view into the given view
 @param aView The view where the render view will added into
 **/
- (void)attachToView:(UIView *)aView;

/**
 * Sets a new size for the rendering view
 @param frame The new frame of the rendering view
 **/
- (void)resizeRenderView:(CGRect)frame;

/**
 * Sets a new root node (may be nil). The rootNode will receive the first [vdNode contact:] call from the engine
 @param aNode The node that should be used as root node
 **/
- (void)setRootNode:(vdNode *)aNode;

/** 
 * Returns the pointer to the root node.
 @return Pointer to the root node or NULL if the kernel has no root node
 **/
- (vdNode *)getRootNode;

/**
 * The engine will write the delta time into the given pointer.
 @param delta The NSTimerInterval where the delta time gets written into.
 **/
- (void)writeDeltaTimeInto:(NSTimeInterval *)delta;

/**
 * Starts the rendering loop.
 **/
- (void)startRendering;

/**
 * Stops the rendering loop.
 **/
- (void)stopRendering;

/**
 * Returns YES if the rendering loop runs, otherwise NO.
 **/
- (BOOL)isRendering;

/**
 * Clears the screen
 **/
- (void)clearScreen;

/**
 * Returns a string with the OpenGL Version + vendor information (like glGetString(GL_VERSION) would do)
 @return Pointer to the String that contains the OpenGL version.
 **/
- (NSString *)getOpenGLVersion;

/**
 * Sets a new orientation for the device.
 @param newOrientation The new orientation
 **/
- (void)setOrientation:(UIInterfaceOrientation)newOrientation;

/**
 * Updates the engine orientation to the current device orientation.
 **/
- (void)updateToOrientation;

/**
 * Draws all nodes on the screen for one time.
 **/
- (void)drawScene;

/**
 * Converts the given UIKit coordinate to an OpenGL coordinate.
 @param convertPoint The point that should be converted
 @return The converted point
 **/
- (CGPoint)convertUIToOpenGL:(CGPoint)convertPoint;

/**
 * Converts the given OpenGL coordinate to an UIKit coordinate.
 @param convertPoint The point that should be converted
 @return The converted point
 **/
- (CGPoint)convertOpenGLToUI:(CGPoint)convertPoint;

/**
 * Applies the current orientation to the point
 @param convertPoint The point that should be converted
 @return The converted point
 **/
- (CGPoint)applyOrientation:(CGPoint)convertPoint;

/**
 * Limits the FPS to the given value. Limiting the FPS will reduce the power consum and can avoid artifacts that appaer when the framerate changes too much between two frames.
 @param FPS The new max FPS.
 @remark The default max FPS is 60
 **/
- (void)setMaxFPS:(int)FPS;

/**
 * Creates a vdKernel with the given API. This can be used to force the engine to use a specific API 
 You can access the vdKernel after this command via [vdKernel sharedKernel]
 @remark When the API isn't supported (either because the hardware isn't cappable or USE_OS_30 or USE_OS_40 isn't defined) the vdKernel tries to fallback to a lower version.
 @return Returns YES on success, otherwise NO
 **/
+ (BOOL)createSharedKernelWithAPI:(vdRenderingAPI)kApi;

/**
 * Adds the given shader into a given render target. The shader will be run after everything has been rendered into the render target and will be render into the default framebuffer!
 @param shader The shader you want to add
 @param target The render target you want to add the shader into
 **/
- (void)addShader:(vdShader *)shader intoRenderTarget:(vdRenderTarget *)target;

/**
 * Removes the given shader from the given render target.
 @param shader The shader you want to remove
 @param target The render target where the shader should be removed from.
 **/
- (void)removeShader:(vdShader *)shader fromRenderTarget:(vdRenderTarget *)target;

/**
 * Creates a new vdRenderTarget. A render target is a frame buffer which renders into a 512x512 texture and can be used to create post processing effects
 @return A new render target
 **/
- (vdRenderTarget *)createRenderTarget;

/**
 * Destroys the given render target
 @param target The target you want to destroy
 @remark The render target must be handled by the vdKernel!
 **/
- (void)deleteRenderTarget:(vdRenderTarget *)target;

/**
 * Releases the vdKernel regardless of its retain count!
 **/
- (void)destroy;

#if defined(USE_OS_32) || defined(USE_OS_40)
/**
 * Creates a vd_queue with the given block.
 @param block A vd_block
 @param timeStep The number of frames which must pass after the dispatching (see [vdKernel dispatchQueue: :]) before the block gets executed.
 @return Pointer to a new vd_queue object
 @remark You need to delete the queue via [vdKernel deleteQueue:] or, when you can't do this, you need to release the block (Block_release)!
 @remark You can get more informations about blocks here: http://developer.apple.com/mac/library/documentation/Cocoa/Conceptual/Blocks/Articles/00_Introduction.html
 **/
- (vd_queue *)createQueue:(vd_block)block withTime:(int)timeStep;

/**
 * Deletes the given vd_queue
 @param queue The queue you want to delete
 @remark Don't call this if you have dispatched the queue and it hasn't been executed yet!
 **/
- (void)deleteQueue:(vd_queue *)queue;

/**
 * Dispatches the given queue. The block of the queue will be executed after the number of frames defined in the queue has been passed.
 @param queue The queue you want to dispatch
 @param yesOrNo If you set this to YES, the queue will be deleted by [vdKernel deleteQueue] right after it has been executed.
 **/
- (void)dispatchQueue:(vd_queue *)queue autoDelete:(BOOL)yesOrNo;
#endif

@end
